import threading
from datetime import datetime, timedelta
import time
import queue
from tools.redis_server import RedisServer
from monitor import Monitor
from tools.util import *
import tools.define as define


class Event:
    def __init__(self, config_data):
        self.config_data = config_data
        self.format_str = self.config_data["format_str"]
        self.window_length = timedelta(minutes=int(config_data['time_interval']))
        self.time_move = timedelta(minutes=int(config_data['time_move']))
        self.gantry_info = self.config_data["gantry_info"]
        self.route_list = self.config_data["route"]
        # 门架数据
        self.gantry_data = {}
        # 数据队列
        self.q = queue.Queue(maxsize=100)
        #
        self.id_dict = {}
        self.info_dict = {}
        self.dist_dict = {}
        self.pass_time_dict = {}
        self.last_type_speed = {}
        self.last_return_data = {}
        self.gantry_ = {}
        self.output = {}
        # 实时时间
        self.current_simulated_time = None
        # 当前日期
        self.day = None
        # 时间窗口
        self.start_time = None
        self.end_time = None
        # 接收的最后一帧数据
        self.last_car = None
        # redis 服务器
        self.redis_server = RedisServer(self.config_data["redis_host"], self.config_data["redis_port"],
                                        self.config_data["redis_pd"], self.config_data["redis_password"])
        # 事件分析
        self.monitor = Monitor(self.config_data["threshold"])
        # 线程管理
        self.thread_flag = True

    def subscribe_data(self):
        channel = self.config_data["redis_channel"]
        self.redis_server.subscribe(channel)
        while True:
            messages = self.redis_server.get_messages()
            if messages:
                try:
                    if self.last_car is None:
                        self.last_car = messages[-1]
                        current_time = messages[-1][define.TIME]
                        self.start_time = datetime.strptime(current_time, self.format_str)
                        self.current_simulated_time = self.start_time
                        self.end_time = self.start_time + self.window_length
                    elif messages[-1] == self.last_car:
                        continue
                    else:
                        current_time = messages[-1][define.TIME]
                        self.current_simulated_time = datetime.strptime(current_time, self.format_str)
                        # 逆序遍历messages
                        i = -1
                        while messages[i] != self.last_car and i > -len(messages):
                            self.q.put(messages[i], block=False)
                            i -= 1
                        self.last_car = messages[-1]
                except Exception as e:
                    print("解析数据失败！！！")
            time.sleep(0.1)
            # 退出线程
            if not self.thread_flag:
                break

    def init(self):
        info_dict = {}
        self.output["data"] = {}
        self.output["gantry_id"] = {}
        for info in self.gantry_info.keys():
            self.last_type_speed[info] = {}
            self.output["data"][info] = {}
            self.gantry_data[info] = {
                "current_up_list": [],
                "current_down_list": [],
                "up_data_dict": {},
                "down_data_dict": {},
                "detention_dict": {},
                "vehicle_move": {}
            }
            self.dist_dict[info] = self.gantry_info[info]["dist"]
            self.pass_time_dict[info] = self.gantry_info[info]["car_pass_time"]
            if info[0] not in self.info_dict:
                self.info_dict[info[0]] = []
            self.info_dict[info[0]].append(info)
            info_dict[info] = self.gantry_info[info]["up_gantry_id"] + self.gantry_info[info]["down_gantry_id"]
        # 获取门架ID对应的路段编号
        self.id_dict = format_conversion(info_dict)
        for id in self.id_dict.keys():
            self.gantry_[id] = 0
        # 实时监听最新门架数据
        threading.Thread(target=self.subscribe_data).start()

    def add_message(self):
        while not self.q.empty():
            message = self.q.get(timeout=0.1)
            gantry_id = message[define.GANTRY]
            if gantry_id in self.id_dict:
                self.gantry_[gantry_id] += 1
                info_list = self.id_dict[gantry_id]
                for info in info_list:
                    if gantry_id in self.gantry_info[info]["up_gantry_id"]:
                        current_time = datetime.strptime(message[define.TIME], self.format_str)
                        vlp = message[define.PLATE]
                        self.gantry_data[info]["current_up_list"].append(current_time)
                        self.gantry_data[info]["up_data_dict"][vlp] = {define.TYPE: message[define.TYPE],
                                                                       define.TIME: current_time}
                    elif gantry_id in self.gantry_info[info]["down_gantry_id"]:
                        current_time = datetime.strptime(message[define.TIME], self.format_str)
                        vlp = message[define.PLATE]
                        self.gantry_data[info]["current_down_list"].append(current_time)
                        self.gantry_data[info]["down_data_dict"][vlp] = {define.TYPE: message[define.TYPE],
                                                                         define.TIME: current_time}

    def expire_clear(self, start_time, end_time):
        # 清除过期数据(一小时前未匹配上的上游门架数据)
        for info in self.gantry_data.keys():
            save_time = self.gantry_info[info]["car_pass_time"] + self.config_data["time_move"]
            i = 0
            while i < len(self.gantry_data[info]["current_up_list"]):
                if self.gantry_data[info]["current_up_list"][i] < start_time:
                    i += 1
                else:
                    break
            self.gantry_data[info]["current_up_list"] = self.gantry_data[info]["current_up_list"][i:]
            j = 0
            while j < len(self.gantry_data[info]["current_down_list"]):
                if self.gantry_data[info]["current_down_list"][j] < start_time:
                    j += 1
                else:
                    break
            self.gantry_data[info]["current_down_list"] = self.gantry_data[info]["current_down_list"][j:]

            del_up_vlp_list = []
            up_vlp_list = list(self.gantry_data[info]["up_data_dict"].keys())
            for vlp in up_vlp_list:
                data_time = self.gantry_data[info]["up_data_dict"][vlp][define.TIME]
                if data_time < end_time - timedelta(minutes=save_time):
                    del_up_vlp_list.append(vlp)
                else:
                    break
            for vlp in del_up_vlp_list:
                up_vlp = self.gantry_data[info]["up_data_dict"][vlp]
                self.gantry_data[info]["detention_dict"][vlp] = {"time": up_vlp[define.TIME],
                                                                 "info": info,
                                                                 "type": up_vlp[define.TYPE]}
                del self.gantry_data[info]["up_data_dict"][vlp]
            del_down_vlp_list = []
            down_vlp_list = list(self.gantry_data[info]["down_data_dict"].keys())
            for vlp in down_vlp_list:
                data_time = self.gantry_data[info]["down_data_dict"][vlp][define.TIME]
                if data_time < start_time:
                    del_down_vlp_list.append(vlp)
                else:
                    break
            for vlp in del_down_vlp_list:
                del self.gantry_data[info]["down_data_dict"][vlp]

        if self.day is None:
            self.day = self.current_simulated_time.day
        else:
            day = self.current_simulated_time.day
            if self.day == day:
                for gantry_id in self.gantry_.keys():
                    self.gantry_[gantry_id] = 0

    def type_analysis(self, info):
        type_speed_dict = {}
        type_pass_rate_dict = {}
        down_data_dict = self.gantry_data[info]["down_data_dict"]
        up_data_dict = self.gantry_data[info]["up_data_dict"]
        vlp_list = down_data_dict.keys()
        for vlp in vlp_list:
            dist = None
            up_time = None
            down_time = down_data_dict[vlp][define.TIME]
            vehicle_type = down_data_dict[vlp][define.TYPE]
            # 查找上游门架对应车牌出现时间，计算平均速度
            if vlp in up_data_dict.keys():
                up_time = up_data_dict[vlp][define.TIME]
                dist = self.dist_dict[info]
                del self.gantry_data[info]["up_data_dict"][vlp]
            else:
                for k in self.gantry_data.keys():
                    if int(k[:2]) >= int(info[:2]):
                        break
                    detention_dict = self.gantry_data[k]["detention_dict"]
                    if vlp in detention_dict.keys():
                        up_time = detention_dict[vlp]["time"]
                        info1 = detention_dict[vlp]["info"]
                        dist = self.get_all_dist(info1, info)
                        del self.gantry_data[k]["detention_dict"][vlp]
                        break
            if dist is not None:
                dt = down_time - up_time
                dt_hours = dt.total_seconds() / 3600
                speed = round((dist / dt_hours), 2)
                if vehicle_type not in type_speed_dict.keys():
                    type_speed_dict[vehicle_type] = {}
                    type_speed_dict[vehicle_type]["speed_list"] = []
                    type_speed_dict[vehicle_type]["slow_level_list"] = []
                type_speed_dict[vehicle_type]["speed_list"].append(speed)

                if vehicle_type not in type_pass_rate_dict.keys():
                    type_pass_rate_dict[vehicle_type] = {}
                    type_pass_rate_dict[vehicle_type]["value"] = []
                    type_pass_rate_dict[vehicle_type]["vaild_count"] = 0
                    type_pass_rate_dict[vehicle_type]["count"] = 0
                type_pass_rate_dict[vehicle_type]["value"].append(int(dt_hours * 60))
                type_pass_rate_dict[vehicle_type]["count"] += 1

        # 计算平均速度
        for vehicle_type in type_speed_dict.keys():
            speed_list = type_speed_dict[vehicle_type]["speed_list"].copy()
            mean_speed = sum(speed_list) / len(speed_list)
            type_speed_dict[vehicle_type]["mean_speed"] = round(mean_speed, 2)
            self.last_type_speed[info][vehicle_type] = type_speed_dict[vehicle_type]["mean_speed"]
            type_speed_dict[vehicle_type]["count"] = len(speed_list)
            # 判断是否慢行
            for i in range(len(speed_list)):
                slow_level = 0
                if (speed_list[i] * 5) < mean_speed:
                    slow_level = 4
                elif (speed_list[i] * 2) < mean_speed:
                    slow_level = 3
                elif (speed_list[i] * 1.6) < mean_speed:
                    slow_level = 2
                elif (speed_list[i] * 1.2) < mean_speed:
                    slow_level = 1
                type_speed_dict[vehicle_type]["slow_level_list"].append(slow_level)
            # 统计慢行小客车的数量
            slow_level_list = type_speed_dict[vehicle_type]["slow_level_list"].copy()
            level_0 = slow_level_list.count(0)
            level_1 = slow_level_list.count(1)
            level_2 = slow_level_list.count(2)
            level_3 = slow_level_list.count(3)
            level_4 = slow_level_list.count(4)
            type_speed_dict[vehicle_type]["slow_level_count"] = [level_0, level_1, level_2, level_3, level_4]

        # 计算通过率
        car_pass_time = self.pass_time_dict[info]
        for vehicle_type in type_pass_rate_dict.keys():
            pass_rate_list = type_pass_rate_dict[vehicle_type]["value"].copy()
            for i in range(len(pass_rate_list)):
                if pass_rate_list[i] <= car_pass_time:
                    type_pass_rate_dict[vehicle_type]["vaild_count"] += 1

        return type_speed_dict, type_pass_rate_dict

    def get_all_dist(self, info1, info2):
        dist = 0
        info_list = []
        for route in self.route_list:
            if info1 in route and info2 in route:
                start_index = route.index(info1)
                end_index = route.index(info2)
                info_list = route[start_index:end_index+1]
                break
        for i in info_list:
            dist += self.dist_dict[i]

        return dist

    def move_vehicle(self):
        for info in self.gantry_data.keys():
            for vehicle in self.gantry_data[info]["up_data_dict"].keys():
                if vehicle not in self.gantry_data[info]["vehicle_move"].keys():
                    veh = self.gantry_data[info]["up_data_dict"][vehicle]
                    self.gantry_data[info]["vehicle_move"][vehicle] = {"type": veh[define.TYPE], "dist": 0}
                else:
                    ty = self.gantry_data[info]["vehicle_move"][vehicle]["type"]
                    if ty in self.last_type_speed[info].keys():
                        last_speed = self.last_type_speed[info][ty]
                    else:
                        last_speed = self.gantry_info[info]["car_min_speed"]
                    self.gantry_data[info]["vehicle_move"][vehicle]["dist"] += round((last_speed / 3600), 4)

    def data_analysis(self):
        return_data = {}
        if self.current_simulated_time is not None and self.current_simulated_time > self.end_time:
            # 移动时间窗口
            self.start_time += self.time_move
            self.end_time += self.time_move

            for info in self.gantry_data.keys():
                return_data[info] = {}
                up_flow_num = len(self.gantry_data[info]["current_up_list"])
                down_flow_num = len(self.gantry_data[info]["current_down_list"])
                return_data[info]["data"] = {"time": self.end_time - self.time_move,
                                             "up_flow_num": up_flow_num,
                                             "down_flow_num": down_flow_num,
                                             "type_speed_dict": {},
                                             "type_pass_rate_dict": {}}
                return_data[info]["info"] = self.gantry_info[info]
                type_speed_dict, type_pass_rate_dict = self.type_analysis(info)
                return_data[info]["data"]["type_speed_dict"] = type_speed_dict
                return_data[info]["data"]["type_pass_rate_dict"] = type_pass_rate_dict

            # 清除过期数据(一小时前未匹配上的上游门架数据)
            self.expire_clear(self.start_time, self.end_time)

        # 添加新数据
        self.add_message()

        # 区间车辆移动
        self.move_vehicle()

        return return_data

    def run(self):
        while True:
            return_data = self.data_analysis()
            if not self.last_return_data:
                self.last_return_data = return_data
            if return_data and self.last_return_data:
                for info in self.gantry_data.keys():
                    data = return_data[info]
                    last_data = self.last_return_data[info]
                    self.monitor.run(data, last_data)

                self.last_return_data = return_data
            for info in self.output["data"].keys():
                if "1" not in self.last_type_speed[info]:
                    mean_speed = self.gantry_info[info]["car_min_speed"]
                else:
                    mean_speed = self.last_type_speed[info]["1"]
                self.output["data"][info]["interval_num"] = 1
                self.output["data"][info]["detention_num"] = 1
                self.output["data"][info]["mean_speed"] = mean_speed
                self.output["data"][info]["vehicle_move"] = self.gantry_data[info]["vehicle_move"].copy()
                self.output["data"][info]["detention_dict"] = self.gantry_data[info]["detention_dict"].copy()
            self.output["gantry_id"] = self.gantry_

            time.sleep(1)
